use crate::error::Result;
use rusqlite::{types::Value as SqliteValue, ToSql};
use rusqlite::{Connection, Statement};
use serde_json::{Number, Value as JsonValue};
use std::collections::hash_map::Values;
use std::collections::HashMap;
use crate::Error;

pub fn get_columns_name(statement: &Statement<'_>) -> Vec<String> {
    let mut columns = Vec::<String>::new();
    for name in statement.column_names() {
        columns.push(name.to_string());
    }
    columns
}

pub fn convert_to_json(value: Values<&str, JsonValue>) -> Result<Vec<Box<dyn ToSql>>> {
    let mut result: Vec<Box<dyn ToSql>> = Vec::new();
    for val in value {
        match val {
            JsonValue::Null => {
                result.push(Box::new(SqliteValue::Null));
            }
            JsonValue::Number(n) => {
                result.push(Box::new(n.as_f64().unwrap()));
            }
            JsonValue::String(s) => {
                result.push(Box::new(SqliteValue::Text(s.to_string())));
            }
            _ => {}
        }
    }
    Ok(result)
}

pub fn query_data(
    connection: &Connection,
    table: &str,
    filter: &str,
    columns: Vec<&str>,
) -> Result<Vec<HashMap<String, JsonValue>>> {
    let mut sql = if columns.is_empty() {
        format!("SELECT * FROM {}", table)
    } else {
        format!("SELECT {} FROM {}", columns.join(","), table)
    };

    if !filter.is_empty() {
        sql.push_str(&format!(" WHERE {}", filter));
    }

    let mut stmt = connection.prepare(&sql)?;
    let columns = get_columns_name(&stmt);
    let mut result = Vec::<HashMap<String, JsonValue>>::new();
    let mut rows = stmt.query([])?;
    while let Ok(Some(row)) = rows.next() {
        let mut map = HashMap::new();
        for (index, name) in columns.iter().enumerate() {
            let value = row.get_ref(index).map_err(|error| Error::DatabaseQueryError(error.to_string()))?;
            let value = match SqliteValue::from(value) {
                SqliteValue::Null => JsonValue::Null,
                SqliteValue::Integer(value) => JsonValue::Number(value.into()),
                SqliteValue::Real(value) => JsonValue::Number(Number::from_f64(value).unwrap()),
                SqliteValue::Text(value) => JsonValue::String(value),
                SqliteValue::Blob(value) => JsonValue::Array(
                    value
                        .iter()
                        .map(|byte| JsonValue::Number((*byte).into()))
                        .collect(),
                ),
            };
            map.insert(name.clone(), value);
        }
        result.push(map);
    };
    Ok(result)
}
